#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "esp_log.h"

#include "Drivers/uart_drv.h"
#include "Drivers/wifi_drv.h"
#include "Drivers/my_xmodem.h"
#include "Drivers/smartconfig_wifi.h"
#include "Drivers/sntp_time_drv.h"
#include "Drivers/spiffs_drv.h"

#include "App/uart_data.h"
#include "App/ota_task.h"
#include "App/http_client_task.h"
#include "App/udp_recipe_fetch.h"

#include "tools.h"
#define TAG   "uart_data"

#define FREAM_HEADER_0      0xaa                //菜谱数据传输帧头
#define FREAM_HEADER_1      0x55

#define FRAME_TAIL_0        0x55                //菜谱数据传输帧尾
#define FRAME_TAIL_1        0xaa

#define UPDATE_HEADER_0     0x01                //获取版本号帧头
#define UPDATE_HEADER_1     0x03

#define RECIPE_ID_LEN        10                 //菜谱id长度
#define BUF_SIZE             256                //数据缓存区大小

#define CAL_SEND_LEN(respLen)           (6 + 10 + respLen + 3)      //计算发送数据长度
                          
static uint16_t g_STC_Version;                                  //stc的版本号

static void DataHandler(uint8_t const *data, int len);          //菜谱数据处理
static void UpdateHandler(void);                                //升级数据处理
static void getLocalRecipe(char *request);                      //获取本地菜谱

/**
 * @brief UART数据接收任务函数
 *
 * 该任务负责接收和处理UART数据，主要功能包括：
 * - 接收并解析菜谱数据(FREAM_HEADER_0/1开头)
 * - 获取STC版本号(UPDATE_HEADER_0/1开头)
 * - 处理STC固件升级指令("MUSTUPDATE"/"UPDATEMA??")
 *
 * @param param 任务参数(未使用)
 * @note 任务会持续运行
 * @note UART接收缓冲区大小为UART_BUF_SIZE
 * @note 接收超时时间为20ms
 */
void UartDataRecvTask(void *param)
{
    ESP_LOGI(TAG, "uart_data_recv_task start");
    uint8_t buffer[UART_BUF_SIZE] = {0};
    uint32_t len = 0;

    while(1)
    {
       len = uart_read_bytes(UART_DATA_NUM, buffer, UART_BUF_SIZE, 20 / portTICK_PERIOD_MS);
       
       if(!len) continue;
       //log
       printf("uart data recv:\n");
       for (size_t i = 0; i < len; i++)
       {
            printf("%02x ", buffer[i]);
       }
       printf("\n");
       
       if(buffer[0] == FREAM_HEADER_0 && buffer[1] == FREAM_HEADER_1)            //菜谱数据  
       {
            char check = check_sum((char*)buffer, len - 3);
            if(check != buffer[len - 3]) 
            {
                ESP_LOGI(TAG, "check error, my check: %x", check);
                continue;
            }
            // DataHandler(buffer + 6, len - 9);
            DataHandler(buffer, len);                                           //菜谱数据处理
       }

       if(buffer[0] == UPDATE_HEADER_0 && buffer[1] == UPDATE_HEADER_1)         //获取版本号
       {
            if(crc16_modbus(buffer, len) == 0)
            {
                g_STC_Version = (uint16_t)buffer[3] << 8 | buffer[4];
                GetVersionSuccess = true;
            }
       }
       if(strstr((char*)buffer, (char*)"MUSTUPDATE") ||                         //stc升级确认
            strstr((char*)buffer, (char*)"UPDATEMA??"))
       {
            UpdateHandler();                                                    //升级数据处理
       }


    //    uart_write_bytes(UART_DATA_NUM,buffer,len);

    }
    vTaskDelete(NULL);
}

/**
 * @brief 处理串口接收到的数据帧
 *
 * 根据数据帧中的命令类型(order)执行不同的操作:
 * - RESET_WIFI: 重置 WiFi 配网
 * - GET_RECIPE: 获取菜谱数据,优先从服务器获取,失败则获取本地菜谱
 * - NETWORK_STATUS: 响应网络连接状态
 * - GET_NET_TIME: 获取并返回网络时间
 *
 * @param data 接收到的数据帧指针
 * @param len 数据帧长度
 *
 * @note 数据帧格式:
 * 帧头(AA 55) + 命令ID + 数据内容 + 校验和 + 帧尾(55 AA)
 */
static void DataHandler(uint8_t const *data, int len)
{
    // AA55 10 0001 05   43 32 34 31 31 30 30 30 30 31      55     66 55AA                                   //主板请求内容，帧头 + id + 其它内容 + 校验 + 帧尾
    // AA 55 11 00 01 06 43 32 34 31 31 30 30 30 30 31 01 00 C8 00 00 00 00 00 00 00 C4 03 84 27 55 AA       //响应内容，    帧头 + id + 菜谱内容 + 校验 + 帧尾
    //                                                 01 00 C8 00 00 00 00 00 00 00 C4 03 84                //服务器获取到的菜谱内容, 工序数量 + 工序1...

    char respBuffer[UART_BUF_SIZE];     //接收服务器的内容
    char sendBuffer[UART_BUF_SIZE];     //发送给主板的内容
    char *pSend = sendBuffer;
    int respLen = 0;
    int sendLen = 0;
    uint16_t order = data[3] << 8 | data[4];

    switch (order)
    {
        case RESET_WIFI:
            if(SmartConfigStart() == ESP_OK)
                uart_write_bytes(UART_DATA_NUM, data, len);
            break;
        
        case GET_RECIPE:

            running_err_t err = getRecipeFromDS((char*)data + 6, respBuffer, len - 9, &respLen);            //从ds中获取菜谱
            // if(running.IsConnected == false || running.IsConnectServer == false)
            // {
            //     return;
            // }      
            if(err == ERR_DS_)                      //服务器错误
            {
                ESP_LOGI(TAG, "Find Local recipes");
                getLocalRecipe((char*)data);        //获取本地菜谱
                return;
            }      
            if(err == ERR_NONE_)                    //获取成功
            {
                char processesNum = respBuffer[0];
                char recipeID[RECIPE_ID_LEN] = {0};
                *pSend++ = FREAM_HEADER_0;
                *pSend++ = FREAM_HEADER_1;                
                *pSend++ = processesNum * 12 + 5;
                *pSend++ = 0x00;
                *pSend++ = 0x01;
                *pSend++ = 0x06;

                memcpy(recipeID, data + 6, RECIPE_ID_LEN); 
                memcpy(pSend, recipeID, RECIPE_ID_LEN);            pSend += RECIPE_ID_LEN;
                memcpy(pSend, respBuffer, respLen);                pSend += respLen;

                sendLen =  CAL_SEND_LEN(respLen);

                *pSend++ = check_sum(sendBuffer, sendLen - 3);
                *pSend++ = FRAME_TAIL_0;
                *pSend++ = FRAME_TAIL_1;
                uart_write_bytes(UART_DATA_NUM, sendBuffer, sendLen); 
                pSend = sendBuffer;

                //////////////保存本地菜谱////////////////
                char val_str[MENUMAX_LINE];
                for(int i = 0;i < respLen;i++){

                    sprintf(val_str + (i * 2),"%02x",respBuffer[i]);
                }
                setLocalRecipe(recipeID, val_str);  
            }
            else if(err == ERR_ID_)         SEND_ID_ERR_MESSAGE();              //id错误
                
            else if(err == ERR_OVER_LIFE_)    SEND_OVERLIFE_ERR_MESSAGE();      //菜谱过期
  
            break;

        case NETWORK_STATUS:                                                    //网络状态
            if (data[5] == 0x02 && data[6] == 0x00)
            {
                if(running.IsConnected == false || 
                    running.IsConnectServer == false)               
                    ResponseWifiStatus(WIFI_DISCONNECTED);                      
                else ResponseWifiStatus(NORMAL_RUNNING);

            }
            break;
        case GET_NET_TIME:      //获取时间，不使用的部分，无需理会
            if (data[5] == 0x01)
            {
                if (data[6] == 0x01)
                {
                    char resp_buf[30] = {0xaa, 0x55, 0x00, 0x00, 0x02, 0x02};    
                    char time[15];
                    strcpy(time, GetTimeStrOfSec());

                    memcpy(resp_buf + 6, time, strlen(time));
                    resp_buf[2] = 3 + strlen(time) + 1;     //填入长度，除去帧头帧尾 aa55 55aa 4个字节的长度 + 检验位
                                                            
                    int len = 6 + strlen(time);
                    resp_buf[len] = check_sum(resp_buf, len);
                    resp_buf[len + 1] = FRAME_TAIL_0;
                    resp_buf[len + 2] = FRAME_TAIL_1;
                    uart_write_bytes(UART_DATA_NUM, resp_buf, len + 3);
                }
            }        
            break;
        default:
            break;
    }
}

/**
 * @brief 处理 STC 单片机固件更新
 *
 * 该函数实现了基于 XMODEM 协议的固件更新流程:
 * 1. 等待下载信号量
 * 2. 打开本地 bin 文件
 * 3. 通过 UART 与 STC 单片机建立通信
 * 4. 使用 XMODEM 协议传输固件数据
 * 5. 处理 ACK/NAK 应答
 * 6. 完成传输后发送 EOT 结束符
 *
 * @note 超时时间为 10 秒
 * @note 使用 132 字节的数据包(包含 XMODEM 协议头)
 * @note 传输完成后会设置 OTA_STC_FINISH_BIT 事件标志
 */
static void UpdateHandler(void)
{
    if (xDownloadSemaphore == NULL)
        return;
    if(xSemaphoreTake(xDownloadSemaphore, 10000 / portTICK_PERIOD_MS))//等待下载成功，超时10秒
    {
        FILE *file = fopen(FILE_PATH,"rb");             
        if(file == NULL)  return;                       //bin文件打开失败

        uart_write_bytes(UART_DATA_NUM, "YES",sizeof "YES");        //回复stc准备接收bin

        char buf[132];
        static size_t index = 1;
        char ack[1];
        bool ota_finish = false;

        xmodem_packet_t *xmodem_packet = malloc(sizeof(xmodem_packet_t));       //free
        xmodem_packet->header = XMODEM_SOH;

        ESP_LOGI(TAG,"update start...");

        while (!ota_finish)
        {
            int rec = uart_read_bytes(UART_DATA_NUM,ack,1,1000 /portTICK_PERIOD_MS);
            if(rec > 0)
            switch (ack[0])
            {
            case XMODEM_NAK:
                
                if(!feof(file)){
                    get_xmodem_buf(file,xmodem_packet,index,buf);
                }
                uart_write_bytes(UART_DATA_NUM,buf,XMODEM_PACKET_SIZE + 4);
                break;
            case XMODEM_ACK:

                if(!feof(file))
                {
                    ++index;
                    get_xmodem_buf(file,xmodem_packet,index,buf);
                    uart_write_bytes(UART_DATA_NUM,buf,XMODEM_PACKET_SIZE + 4);
                }
                else
                {
                    char eot[] = {XMODEM_EOT};
                    uart_write_bytes(UART_DATA_NUM,eot,1);
                    ESP_LOGI(TAG,"updatema end");
                    get_xmodem_buf(file,xmodem_packet,-1,buf);      
                    fclose(file);
                    free(xmodem_packet);
                    ota_finish = true;
                    index = 1;
                    vTaskDelay(3500 / portTICK_PERIOD_MS);              //预计最大3.5s升级结束
                    xEventGroupSetBits(OTAEventGroup, OTA_STC_FINISH_BIT);
                    ResponseWifiStatus(NORMAL_RUNNING);                 //发送配网成功指令
                    return;
                }
                break;
            case XMODEM_CAN:
                ota_finish = true;
                index = 1;
                break;
            default:
                break;
            }
        }
 
    }
    ESP_LOGE(TAG,"stc updete time out!");
}

/**
 * @brief 获取STC版本号
 * @param None
 * @return STC版本号
 */
uint16_t GetSTCVersion(void)
{
    return g_STC_Version;
}

/**
 * @brief 响应 WiFi 连接状态
 *
 * @param status WiFi 状态枚举值，包括：
 *              - WIFI_DISCONNECTED: WiFi 断开连接
 *              - SERVER_DISCONNECTED: 服务器断开连接
 *              - SERVER_NOT_EXIST_SN: 服务器不存在序列号
 *              - NORMAL_RUNNING: 正常运行
 *
 * @details 根据不同的 WiFi 状态，通过 UART 发送对应的状态响应数据包
 *         数据包格式: AA 55 05 00 05 03 [状态值] [校验和] 55 AA
 *         状态值包含 WiFi 信号强度(低4位)和连接状态(高4位)
 */
void ResponseWifiStatus(RespStatus_t status)
{

    uint8_t Response[] = {0xaa, 0x55, 0x05, 0x00, 0x05, 0x03, 0x00, 0x00, 0x55, 0xaa};
    uint8_t val = 0;
    uint8_t Signal = 5; // 信号强度取消获取，获取函数在旧版本tools/rssi_to_level()

    switch (status)
    {
    case WIFI_DISCONNECTED || SERVER_DISCONNECTED:
        val = 0x00;
        break;
    case SERVER_NOT_EXIST_SN:
        val = 0xf0;
        break;
    case NORMAL_RUNNING:                //运行正常
        val = 0x10;
        break;    
    default:  
        
        break;
    }
            
    val |= Signal;
    Response[6] = val;
    Response[7] = check_sum((char *)Response, sizeof(Response) - 3);
    uart_write_bytes(UART_DATA_NUM, Response, sizeof(Response));

    // char buffer[UART_BUF_SIZE] = {0};
    // int len = uart_read_bytes(UART_DATA_NUM, buffer, UART_BUF_SIZE, 3000 / portTICK_PERIOD_MS);
    // if(!len)  ResponseWifiStatus(status);
}

/**
 * @brief 获取本地菜谱数据
 *
 * @param request 请求字符串，包含菜谱ID信息
 *
 * @details 该函数执行以下操作:
 *          1. 从请求中提取菜谱ID
 *          2. 根据菜谱ID获取本地菜谱数据
 *          3. 将菜谱数据转换为十六进制格式
 *          4. 按照指定协议格式封装数据
 *          5. 通过串口发送封装后的数据
 *
 * @note 如果获取菜谱失败或数据格式错误，将返回配网失败状态
 */
static void getLocalRecipe(char *request)
{
    char dataStr[BUF_SIZE * 2] = {0};
    uint8_t dataHex[BUF_SIZE] = {0};
    char recipeID[RECIPE_ID_LEN] = {0};
    
    memcpy(recipeID, request + 6, RECIPE_ID_LEN);

    // ESP_LOGI(TAG,"%s",recipeID);
    if(get_local_menu(recipeID,dataStr))
    {
        // printf("%s",dataStr);
        ESP_LOGI(TAG,"get local recipe success");
        if(strlen(dataStr) % 2 != 0)
        {
            ResponseWifiStatus(WIFI_DISCONNECTED);
            return;
        } 
        size_t len = my_strToHex(dataHex, dataStr);

        char sendBuffer[UART_BUF_SIZE];     
        char *pSend = sendBuffer;
        char processesNum = dataHex[0];
        int sendLen = 0;

        *pSend++ = FREAM_HEADER_0;
        *pSend++ = FREAM_HEADER_1;                
        *pSend++ = processesNum * 12 + 5;
        *pSend++ = 0x00;
        *pSend++ = 0x01;
        *pSend++ = 0x06;

        memcpy(pSend, recipeID, RECIPE_ID_LEN);            pSend += RECIPE_ID_LEN;
        memcpy(pSend, dataHex, len);                        pSend += len;

        sendLen =  CAL_SEND_LEN(len);

        *pSend++ = check_sum(sendBuffer, sendLen - 3);
        *pSend++ = FRAME_TAIL_0;
        *pSend++ = FRAME_TAIL_1;
        uart_write_bytes(UART_DATA_NUM, sendBuffer, sendLen); 

    }
    else
        ResponseWifiStatus(WIFI_DISCONNECTED);
}